package org.munoz.shared;

/*
 * This class mutates the state of the game.
 */
/**
 * @author jpm
 *
 */
public class StateChanger {
	private Player req; // Player who is requesting a card.
	private Player giv; // Player who is giving a card.

	/**
	 * Mutates state depending on the move. 
	 * @param state
	 * @param move
	 * @throws IllegalMove
	 */
	public void makeMove(State state, Move move) throws IllegalMove{
		
//		System.out.println("In StateChanger1");
		
		// Is game over?
		if(state.getGameResult()!=null){
			throw new IllegalMove("makeMove Game Result is not null");
		}
		
		// Is the card requested already placed in the table
		if(state.getFourRankCardsPlacedInTable().contains(move.getRank())){
			throw new IllegalMove("makeMove Card is already in table");
		}
		
		// Check if turn is ok. // I commented this for HW#7. I am checking this on the presenter
		if(move.getPlayerId() != state.getTurn()){
			throw new IllegalMove("makeMove Wrong turn");
		}
		
		if(move.getPlayerId() == state.PLAYER1){
			req = state.getPlayers()[state.PLAYER1];
			giv = state.getPlayers()[state.PLAYER2];
		}
		else {
			req = state.getPlayers()[state.PLAYER2];
			giv = state.getPlayers()[state.PLAYER1];
		}
		
		// Check whether requester has the card requested
		boolean reqHasCard = false;
		for(int i = 0; i<req.getNumberOfCardsInHand(); i++) {
			Card c = req.getHand().getDeck(i);
//			System.out.println("Cards in req: " + c.toString());
//			System.out.println("Requested: " + move.getRank());
			if(c.getRank() == move.getRank()){
//				System.out.println("Ranks were equal");
				reqHasCard = true;
			}
		}
		if(!reqHasCard){
//			System.out.println("Throwing exception");
			throw new IllegalMove("makeMove Requester doesn't have card.");
		}
		
		//if giv has card(s) then they move to the req.
		boolean cardWasGiven = false;
		
//		System.out.println("In StateChanger3");

		for(int i = giv.getNumberOfCardsInHand(); i>0; i--){
			Card c = giv.getHand().getDeck(i-1);
			if(c.getRank() == move.getRank()){
				req.getHand().addOneCard(new Card(c.getSuit(), c.getRank()));
				giv.getHand().removeCardDeck(i-1);
				cardWasGiven = true;
			}
		}
		// Check whether giver doesn't have any more cards.
		if(giv.getHand().isEmpty()){
			declareWinner(state);
			return;
		}
		
//		Check whether req got four of same rank. // check if player has 4 cards of the same rank. 
		putGroupOf4CardsSameRankInTable(state, req);
		
		System.out.println("In StateChanger4");

		if(req.getHand().isEmpty()){
			System.out.println("In StateChanger: About to declare winner");
			declareWinner(state);
			return;
		}
		
		if(cardWasGiven){
			System.out.println("In StateChanger: Card was given");
			return;
		}
		else {// elseif giv doesn't have a card with that rank get req card from deck. 
			System.out.println("In StateChanger5");
			Card fromDeck = state.getToDeal().dealCard();
			req.getHand().addOneCard(fromDeck);
			
//			Check if req got four of same rank. // check if player has 4 cards of the same rank. 
			putGroupOf4CardsSameRankInTable(state, req);
			
			if(req.getHand().isEmpty()){
				System.out.println("In StateChanger6");
				declareWinner(state);
				return;
			}
			
			// Check if deck becomes empty.
			if(state.getToDeal().isEmpty()){
//				System.out.println("In StateChanger7");
				declareWinner(state);
				return;
			}
			else if(fromDeck.getRank() == move.getRank()){
//				System.out.println("In StateChanger8");
				return; // No change of turns // Go Fish for req. // if card from deck is card requested, show and keep playing
			}
			else{ // else card is not req, add to hand req and change of turns.
//				System.out.println("In StateChanger9");
				state.setTurn((state.getTurn()+1)%State.MAX_NUM_PLAYERS); // Change turns
				return;
			}
		}		
	}
	/**
	 * Places group of cards of the same rank on the table. Cards are removed from players
	 * and added to the table. 
	 * @param state
	 * @param req
	 */
	private void putGroupOf4CardsSameRankInTable(State state, Player req){
		boolean [] placeInTable = req.check4CardSameRankInHand();
		placeCardsSameRankInTable(state, placeInTable);
	}
	
	/**
	 * Declares the winner of the game. 
	 * @param state
	 */
	private void declareWinner(State state){
		if(state.getPlayers()[state.PLAYER1].getNumberOfRankGroups() > state.getPlayers()[state.PLAYER2].getNumberOfRankGroups()){
			state.setGameResult(new GameResult(state.PLAYER1, GameResultReason.PLAYERHASNOMORECARDS));
		}
		else if(state.getPlayers()[state.PLAYER2].getNumberOfRankGroups() > state.getPlayers()[state.PLAYER1].getNumberOfRankGroups()) {
			state.setGameResult(new GameResult(state.PLAYER2, GameResultReason.PLAYERHASNOMORECARDS));
		}
		else
			state.setGameResult(new GameResult(state.TIE, GameResultReason.BOTHPLAYERSHAVESAMENUMBEROFGROUPCARDS));
	}
	
	/**
	 * Helper 
	 * @param state
	 * @param toPlace
	 */
	private void placeCardsSameRankInTable(State state, boolean [] toPlace){
		for(int i=0; i<toPlace.length; i++){
			if(toPlace[i] == true){
				state.getFourRankCardsPlacedInTable().add(Rank.values()[i]);
				state.getPlayers()[state.getTurn()].add4CardSameRankToTable(Rank.values()[i]);
			}
		}
	}
}
